//ZHANG HAI JUN 710605420@qq.com

#ifndef __Navigation_H__
#define __Navigation_H__



#include <vector>

#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "DetourNavMeshBuilder.h"
#include "DetourTileCache.h"
#include "DetourTileCacheBuilder.h"
#include "fastlz.h"
#include "DetourCommon.h"


static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
static const int MAX_POLYS = 256;
static const int MAX_SMOOTH = 2048;

static const int TILECACHESET_MAGIC = 'T' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'TSET';
static const int TILECACHESET_VERSION = 1;

/// These are just sample areas to use consistent values across the samples.
/// The use should specify these base on his needs.
enum SamplePolyAreas
{
	SAMPLE_POLYAREA_GROUND,
	SAMPLE_POLYAREA_WATER,
	SAMPLE_POLYAREA_ROAD,
	SAMPLE_POLYAREA_DOOR,
	SAMPLE_POLYAREA_GRASS,
	SAMPLE_POLYAREA_JUMP,
};
enum SamplePolyFlags
{
	SAMPLE_POLYFLAGS_WALK = 0x01,		// Ability to walk (ground, grass, road)
	SAMPLE_POLYFLAGS_SWIM = 0x02,		// Ability to swim (water).
	SAMPLE_POLYFLAGS_DOOR = 0x04,		// Ability to move through doors.
	SAMPLE_POLYFLAGS_JUMP = 0x08,		// Ability to jump.
	SAMPLE_POLYFLAGS_DISABLED = 0x10,		// Disabled polygon
	SAMPLE_POLYFLAGS_ALL = 0xffff	// All abilities.
};


enum NavigationPolyAreas
{
	NAVIGATION_POLYAREA_GROUND,
	NAVIGATION_POLYAREA_WATER,
	NAVIGATION_POLYAREA_ROAD,
	NAVIGATION_POLYAREA_DOOR,
	NAVIGATION_POLYAREA_GRASS,
	NAVIGATION_POLYAREA_JUMP,
};

enum NavigationPolyFlags
{
	NAVIGATION_POLYFLAGS_WALK = 0x01,		// Ability to walk (ground, grass, road)
	NAVIGATION_POLYFLAGS_SWIM = 0x02,		// Ability to swim (water).
	NAVIGATION_POLYFLAGS_DOOR = 0x04,		// Ability to move through doors.
	NAVIGATION_POLYFLAGS_JUMP = 0x08,		// Ability to jump.
	NAVIGATION_POLYFLAGS_DISABLED = 0x10,		// Disabled polygon
	NAVIGATION_POLYFLAGS_ALL = 0xffff	// All abilities.
};


struct  NavPoint
{
	dtPolyRef polyRef;
	float point[3];

};

struct LinearAllocator : public dtTileCacheAlloc
{
	unsigned char* buffer;
	size_t capacity;
	size_t top;
	size_t high;

	LinearAllocator(const size_t cap) : buffer(0), capacity(0), top(0), high(0)
	{
		resize(cap);
	}

	~LinearAllocator()
	{
		dtFree(buffer);
	}

	void resize(const size_t cap)
	{
		if (buffer) dtFree(buffer);
		buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
		capacity = cap;
	}

	virtual void reset()
	{
		high = dtMax(high, top);
		top = 0;
	}

	virtual void* alloc(const size_t size)
	{
		if (!buffer)
			return 0;
		if (top + size > capacity)
			return 0;
		unsigned char* mem = &buffer[top];
		top += size;
		return mem;
	}

	virtual void free(void* /*ptr*/)
	{
		// Empty
	}
};


class Navigation
{
protected:
	class dtNavMesh* m_navMesh = nullptr;
	class dtNavMeshQuery* m_navQuery = nullptr;
	//class dtCrowd* m_crowd;

	dtQueryFilter m_filter;
	dtPolyRef m_startRef;
	dtPolyRef m_nearestRef;
	dtPolyRef m_endRef;

	float m_polyPickExt[3];
	float m_nearestPoint[3];

	dtStatus m_pathFindStatus;
	float m_neighbourhoodRadius;

	float m_randomRadius;
	int m_npolys;
	int m_nstraightPath;

	dtPolyRef m_polys[MAX_POLYS];
	dtPolyRef m_parent[MAX_POLYS];
	float m_straightPath[MAX_POLYS * 3];
	unsigned char m_straightPathFlags[MAX_POLYS];
	dtPolyRef m_straightPathPolys[MAX_POLYS];
	dtPolyRef m_pathIterPolys[MAX_POLYS];
	int m_straightPathOptions;

	struct LinearAllocator* m_talloc;
	struct FastLZCompressor* m_tcomp;
	struct MeshTempObstacleProcess* m_tmproc;
	class dtTileCache* m_tileCache;
	
private:

	dtNavMesh* _LoadTile( char* path);
	dtNavMesh* _LoadTileFromBuffer( unsigned char* data, size_t size);

	dtNavMesh* _LoadTempObstacleTile(const char* path);
	dtNavMesh* _LoadTempObstacleTileFromBuffer(unsigned char* data, size_t size);


public: 

	Navigation();
	virtual ~Navigation();

	//dtNavMesh* LoadTileWithCache(const char* path);
	void LoadTile( char* path);
	bool LoadTileFromBuffer( unsigned char* data,size_t size);

	bool LoadTempObstacleTile(const char* path);
	bool LoadTempObstacleTileFromBuffer(unsigned char* data, size_t size);
	
	bool IsStatusSucceed(dtStatus status);
	//dtStatus GetNeighbourhood(const float *startPos,const float *polyPickExt = nullptr);
	dtStatus FindRandomPointAroundCircle(NavPoint centerPos, float radius,NavPoint* outPos);
	dtStatus FindNearestPoly( float* startPos, NavPoint* navPoint,  float *polyPickExt = nullptr);
	dtStatus FindPathStraight( float* startPos,  float* endPos,float *out_list,int* count);
	dtStatus FindRaycast( float* startPos,  float* endPos, float* out_hitPos, float* out_tangent);
	dtStatus FindPolyHeight(float* startPos, float* height);
	
	const int GetTitleIndex(float* startPos);
	const dtMeshTile* GetTileByIndexAt(int i) const;
	const dtMeshTile* GetTileAt(const int x, const int y, const int layer) const;
	int GetTilesAt(const int x, const int y,
		dtMeshTile const** tiles, const int maxTiles) const;
	dtStatus GetPolyFlags(float* startPos, unsigned short* resultFlags) ;

	// Aabb obstacle.
	dtStatus AddBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
	// Box obstacle: can be rotated in Y.
	dtStatus AddBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
	// Cylinder obstacle.
	dtStatus AddObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
	dtStatus RemoveObstacle(const dtObstacleRef ref);
};

#endif // !Navigation+H
